package com.lc.builder;

import com.lc.bean.Constants;
import com.lc.bean.FieldInfo;
import com.lc.bean.TableInfo;
import com.lc.utils.StringUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 功能：
 * 作者：lc
 * 日期：2024/7/23 23:55
 */
public class BuildMapperXml {
    private static final Logger logger = LoggerFactory.getLogger(BuildMapperXml.class);
    private static final String BASE_COLUMN_LIST = "columns";
    private static final String BASE_QUERY_CONDITION = "base_query_condition";
    private static final String QUERY_CONDITION = "query_condition";
    private static final String BASE_QUERY_CONDITION_EXTENDS = "base_query_condition_extends";
    private static final String INSERT_CONDITION = "insert_condition";
    private static final String UPDATE_CONDITION = "update_condition";
    private static final String INSERT_BATCH_SQL = "insert_batch_sql";
    private static final String UPDATE_SQL = "update_sql";

    public static void execute(TableInfo tableInfo) {
        String lowBeanName = StringUtils.lowerCaseFirstLetter(tableInfo.getBeanName()) + "ResultMap";
        String poClass = Constants.PACKAGE_PO + "." + tableInfo.getBeanName();
        File folder = new File(Constants.PATH_MAPPER_XML);
        if (!folder.exists()) {
            folder.mkdirs();
        }
        String mapperName = tableInfo.getBeanName() + Constants.SUFFIX_MAPPER;
        File poFile = new File(folder, mapperName + ".xml");
        OutputStream out = null;
        OutputStreamWriter outw = null;
        BufferedWriter bw = null;
        try {
            out = Files.newOutputStream(poFile.toPath());
            outw = new OutputStreamWriter(out, "utf8");
            bw = new BufferedWriter(outw);
            bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                    "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n" +
                    "<!--namespace:映射文件的命名空间只想的映射器的全名（包名.类名）-->\n");
            bw.write("<mapper namespace=\"" + Constants.PACKAGE_MAPPER + "." + mapperName + "\">\n" +
                    "\t<!--结果映射-->\n");
            bw.write("\t<resultMap id=\"" + lowBeanName + "\" type=\"" + poClass + "\">\n");
            /*遍历tableInfo.getKeyIndexMap()*/
            FieldInfo idField = null;
            Map<String, String> keyTempMap = new HashMap<>();
            for (Map.Entry<String, List<FieldInfo>> entry : tableInfo.getKeyIndexMap().entrySet()) {
                List<FieldInfo> fieldInfoList = entry.getValue();
                // 将字段名填入临时映射中
                for (FieldInfo item : fieldInfoList) {
                    keyTempMap.put(item.getFieldName(), item.getFieldName());
                    // 查找唯一主键
                    if ("PRIMARY".equals(entry.getKey()) && fieldInfoList.size() == 1) {
                        idField = fieldInfoList.get(0);
                        break;  // 找到主键后立即退出循环
                    }
                }
            }

            StringBuilder insertFieldBuffer = new StringBuilder();
            StringBuilder insertPropertyBuffer = new StringBuilder();
            StringBuilder insertOrUpdateBuffer = new StringBuilder();
            StringBuilder updateBuffer = new StringBuilder();
            // 得到响应得字段和映射属性
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                if (fieldInfo.getIsAutoIncrement()) {
                    continue;
                }
                insertFieldBuffer.append("`").append(fieldInfo.getFieldName()).append("`,");
                insertPropertyBuffer.append("#{item.").append(fieldInfo.getPropertyName()).append("},");
                insertOrUpdateBuffer.append("\t\t`").append(fieldInfo.getFieldName()).append("`")
                        .append(" = VALUES(`").append(fieldInfo.getFieldName()).append("`),\n");
                updateBuffer.append("`").append(fieldInfo.getFieldName()).append("` = #{bean.").append(fieldInfo.getPropertyName()).append("},");
            }
            String insertFieldBufferStr = insertFieldBuffer.substring(0, insertFieldBuffer.lastIndexOf(","));
            String insertPropertyBufferStr = insertPropertyBuffer.substring(0, insertPropertyBuffer.lastIndexOf(","));
            String insertOrUpdateBufferStr = insertOrUpdateBuffer.substring(0, insertOrUpdateBuffer.lastIndexOf(","));
            String updateBufferSrt = updateBuffer.substring(0, updateBuffer.lastIndexOf(","));


            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                bw.write("\t\t<!--" + fieldInfo.getComment() + "-->\n");
                String key = "";
                if (idField != null && fieldInfo.getPropertyName().equals(idField.getPropertyName())) {
                    key = "id";
                } else {
                    key = "result";
                }
                bw.write("\t\t<" + key + " column=\"" + fieldInfo.getFieldName() + "\" property=\"" + fieldInfo.getPropertyName() + "\"/>\n");
            }
            bw.write("\t</resultMap>\n");

            /*重用sql*/
            bw.write("\t\t<!--复用查询列-->\n\t\t<sql id=\"" + BASE_COLUMN_LIST + "\">\n");
            StringBuilder columnsBuilder = new StringBuilder();
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                columnsBuilder.append(fieldInfo.getFieldName() + ",");
            }
            String columnsBuilderString = columnsBuilder.substring(0, columnsBuilder.lastIndexOf(","));
            bw.write("\t\t" + columnsBuilderString);
            bw.write("\n\t\t</sql>\n");
            bw.write("\t\t<!--复用条件查询-->\n\t\t<sql id=\"" + BASE_QUERY_CONDITION + "\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                bw.write("\t\t\t<if test=\"query." + fieldInfo.getPropertyName() + "!= null " +
                        "and query." + fieldInfo.getPropertyName() + "!=''\">\n");
                bw.write("\t\t\t\tAND " + fieldInfo.getFieldName() + "=#{" + fieldInfo.getPropertyName() + "}\n");
                bw.write("\t\t\t</if>\n");
            }
            bw.write("\n\t\t</sql>\n");


            /*扩展查询条件*/
            bw.write("\t\t<!--扩展查询条件-->\n" +
                    "\t<sql id=\"" + BASE_QUERY_CONDITION_EXTENDS + "\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldExtends()) {
                String andWhere = "";
                if (ArrayUtils.contains(Constants.SQL_STRING_TYPE, fieldInfo.getSqlType())) {
                    andWhere = "\t\t\tAND " + fieldInfo.getFieldName() + " LIKE concat('%',#{query." +
                            fieldInfo.getPropertyName() + "},'%')\n";
                } else if (ArrayUtils.contains(Constants.SQL_DATE_TIME_TYPES, fieldInfo.getSqlType())
                        || ArrayUtils.contains(Constants.SQL_DATE_TYPES, fieldInfo.getSqlType())) {
                    if (fieldInfo.getPropertyName().endsWith(Constants.SUFFIX_TABLE_QUERY_START)) {
                        andWhere = "\t\t\t<![CDATA[ AND " + fieldInfo.getFieldName() + ">= str_to_date(#{query." +
                                fieldInfo.getPropertyName() +
                                "}, '%Y-%m-%d') ]]>\n";
                    } else {
                        andWhere = "\t\t\t<![CDATA[ AND " + fieldInfo.getFieldName() + " < date_sub(str_to_date(#{query." +
                                fieldInfo.getPropertyName() + "},'%Y-%m-%d')" +
                                ",interval 1 day) ]]>\n";
                    }
                }
                bw.write("\t\t<if test=\"query." + fieldInfo.getPropertyName() + "!= null " +
                        "and query." + fieldInfo.getPropertyName() + "!=''\">\n");
                bw.write(andWhere);
                bw.write("\t\t</if>\n");
            }
            bw.write("\t</sql>\n");


            /*通用查询条件*/
            bw.write("\t<!--扩展查询条件-->\n" +
                    "\t<sql id=\"" + QUERY_CONDITION + "\">\n" +
                    "\t\t<where>\n");
            bw.write("\t\t\t<include refid=\"" + BASE_QUERY_CONDITION + "\"/>\n");
            bw.write("\t\t\t<include refid=\"" + BASE_QUERY_CONDITION_EXTENDS + "\"/>\n");
            bw.write("\t\t</where>\n\t</sql>\n");


            /* 通用插入条件*/
            bw.write("\t<!--通用插入条件-->\n" +
                    "\t<sql id=\"" + INSERT_CONDITION + "\">\n");
            bw.write("\t\t<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                /*  判断是否为Date或者DateTime类型*/
                if (fieldInfo.getJavaType().equals("Date") || fieldInfo.getJavaType().equals("DateTime")) {
                    bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">\n" +
                            "\t\t\t\t`" + fieldInfo.getFieldName() + "`,\n" +
                            "\t\t\t</if>\n");
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null " +
                        "and bean." + fieldInfo.getPropertyName() + " !=''\">\n" +
                        "\t\t\t\t`" + fieldInfo.getFieldName() + "`,\n" +
                        "\t\t\t</if>\n");
            }
            bw.write("\t\t</trim>\n\t\t<trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                /*判断是否为Date或者DateTime类型*/
                if (fieldInfo.getJavaType().equals("Date") || fieldInfo.getJavaType().equals("DateTime")) {
                    bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">\n" +
                            "\t\t\t\t#{bean." + fieldInfo.getPropertyName() + "},\n" +
                            "\t\t\t</if>\n");
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null " +
                        "and bean." + fieldInfo.getPropertyName() + " !=''\">\n" +
                        "\t\t\t\t#{bean." + fieldInfo.getPropertyName() + "},\n" +
                        "\t\t\t</if>\n");
            }
            bw.write("\t\t</trim>\n" +
                    "\t</sql>\n");

            /* 通用更新条件 ON DUPLICATE KEY UPDATE*/
            bw.write("\t<!--通用更新条件-->\n" +
                    "\t<sql id=\"" + UPDATE_CONDITION + "\">\n");
            bw.write("\t\t<trim prefix=\"\" suffix=\"\" suffixOverrides=\",\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                // 判断是否为主键或者唯一索引
                if (keyTempMap.get(fieldInfo.getFieldName()) != null) {
                    continue;
                }
                /*判断是否为Date或者DateTime类型*/
                if (fieldInfo.getJavaType().equals("Date") || fieldInfo.getJavaType().equals("DateTime")) {
                    bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">\n" +
                            "\t\t\t\t`" + fieldInfo.getFieldName() +
                            "` = VALUES(`" + fieldInfo.getFieldName() + "`),\n" +
                            "\t\t\t</if>\n");
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null " +
                        "and bean." + fieldInfo.getPropertyName() + " !=''\">\n" +
                        "\t\t\t\t`" + fieldInfo.getFieldName() +
                        "` = VALUES(`" + fieldInfo.getFieldName() + "`),\n" +
                        "\t\t\t</if>\n");

            }
            bw.write("\t\t</trim>\n" +
                    "\t</sql>\n");

            /*批量更新修改条件*/
            bw.write("\t<!--批量更新修改条件-->\n" +
                    "\t<sql id=\"" + INSERT_BATCH_SQL + "\">\n" +
                    "\t\tINSERT INTO " + tableInfo.getTableName() + "(" + insertFieldBufferStr + ") VALUES\n" +
                    "\t\t<foreach collection=\"list\" item=\"item\" separator=\",\">\n" +
                    "\t\t(" + insertPropertyBufferStr + "\n\t\t</foreach>\n");
            bw.write("\t</sql>\n");

            /*更新*/
            bw.write("\t<!--更新条件-->\n" +
                    "\t<sql id=\"" + UPDATE_SQL + "\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                // 判断是否为主键或者唯一索引
                if (keyTempMap.get(fieldInfo.getFieldName()) != null) {
                    continue;
                }
                /*判断是否为Date或者DateTime类型*/
                if (fieldInfo.getJavaType().equals("Date") || fieldInfo.getJavaType().equals("DateTime")) {
                    bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null\">\n" +
                            "\t\t\t\t`" + fieldInfo.getFieldName() +
                            "` = #{bean." + fieldInfo.getPropertyName() + "},\n" +
                            "\t\t\t</if>\n");
                    continue;
                }
                bw.write("\t\t\t<if test=\"bean." + fieldInfo.getPropertyName() + " != null " +
                        "and bean." + fieldInfo.getPropertyName() + " !=''\">\n" +
                        "\t\t\t\t`" + fieldInfo.getFieldName() +
                        "` = #{bean." + fieldInfo.getPropertyName() + "},\n" +
                        "\t\t\t</if>\n");
            }
            bw.write("\t</sql>\n");

            /*查询列表*/
            bw.write("\t<!--查询列表-->\n");
            if (Constants.IGNORE_COLUMN_DATA) {
                bw.write("\t<select id=\"selectList\" resultMap=\"" + lowBeanName + "\">\n" +
                        "\t\tSELECT\n" +
                        "\t\t<include refid=\"" + BASE_COLUMN_LIST + "\"/>\n" +
                        "\t\tFROM " + tableInfo.getTableName() + "\n" +
                        "\t\tWHERE del_flag = '1'\n" +
                        "\t\t<if test=\"query.orderBY!=null\">\n" +
                        "\t\t\tORDER BY ${query.orderBy}\n" +
                        "\t\t</if>\n" +
                        "\t\t<if test=\"query.simplePage!=null\">\n" +
                        "\t\t\tlimit #{query.simplePage.start},#{query.simplePage.end}\n" +
                        "\t\t</if>\n" +
                        "\t</select>\n");
            }else {
                bw.write("\t<select id=\"selectList\" resultMap=\"" + lowBeanName + "\">\n" +
                        "\t\tSELECT\n" +
                        "\t\t<include refid=\"" + BASE_COLUMN_LIST + "\"/>\n" +
                        "\t\tFROM " + tableInfo.getTableName() + "\n" +
                        "\t\t<include refid=\"" + QUERY_CONDITION + "\"/>\n" +
                        "\t\t<if test=\"query.orderBY!=null\">\n" +
                        "\t\t\tORDER BY ${query.orderBy}\n" +
                        "\t\t</if>\n" +
                        "\t\t<if test=\"query.simplePage!=null\">\n" +
                        "\t\t\tlimit #{query.simplePage.start},#{query.simplePage.end}\n" +
                        "\t\t</if>\n" +
                        "\t</select>\n");
            }

            /*查询数量*/
            bw.write("\t<!--查询数量-->\n");
            bw.write("\t<select id=\"selectCount\" resultType=\"java.lang.Integer\">\n" +
                    "\t\tSELECT count(1) FROM " + tableInfo.getTableName() + "\n" +
                    "\t\t<include refid=\"query_condition\"/>\n" +
                    "\t</select>\n");

            /* 插入（匹配有值的字段）*/
            FieldInfo autoIncrement = null;
            bw.write("\t<!--插入（匹配有值的字段）-->\n");
            bw.write("\t<insert id=\"insert\" parameterType=\"" +
                    Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">\n");
            for (FieldInfo fieldInfon :
                    tableInfo.getFieldList()) {
                if (fieldInfon.getIsAutoIncrement() != null && fieldInfon.getIsAutoIncrement()) {
                    autoIncrement = fieldInfon;
                    break;
                }
            }
            if (autoIncrement != null) {
                bw.write("\t\t<selectKey keyProperty=\"bean." + autoIncrement.getFieldName() +
                        "\" resultType=\"" + autoIncrement.getJavaType() + "\" order=\"AFTER\">\n" +
                        "\t\t\tSELECT LAST_INSERT_ID()\n" +
                        "\t\t</selectKey>\n" +
                        "\t\tINSERT INTO " + tableInfo.getTableName() + "\n" +
                        "\t\t<include refid=\"" + INSERT_CONDITION + "\"/>\n");
            }

            bw.write("\t</insert>\n");

            /* 更新或者添加*/
            bw.write("\t<!--插入或者更新 （匹配有值的字段-->\n");
            bw.write("\t<insert id=\"insertOrUpdate\" parameterType=\"" +
                    Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">\n");
            for (FieldInfo fieldInfo :
                    tableInfo.getFieldList()) {
                if (fieldInfo.getIsAutoIncrement() != null && fieldInfo.getIsAutoIncrement()) {
                    autoIncrement = fieldInfo;
                    break;
                }
            }
            if (autoIncrement != null) {
                bw.write("\t\t<selectKey keyProperty=\"bean." + autoIncrement.getFieldName() +
                        "\" resultType=\"" + autoIncrement.getJavaType() + "\" order=\"AFTER\">\n" +
                        "\t\t\tSELECT LAST_INSERT_ID()\n" +
                        "\t\t</selectKey>\n" +
                        "\t\tINSERT INTO " + tableInfo.getTableName() + "\n" +
                        "\t\t<include refid=\"" + INSERT_CONDITION + "\"/>\n" +
                        "\t\tON DUPLICATE KEY UPDATE\n" +
                        "\t\t<include refid=\"" + UPDATE_CONDITION + "\"/>\n");
            }
            bw.write("\t</insert>\n");

            /*批量添与批量插入修改*/
            bw.write("\t<!--批量添加（插入）-->\n");

            bw.write("\t<insert id=\"insertBatch\" parameterType=\"" +
                    Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">\n" +
                    "\t\t<include refid=\"" + INSERT_BATCH_SQL + "\"/>\n");
            bw.write("\t</insert>\n");

            /*批量添加或者插入*/
            bw.write("\t<!--批量添加（插入）-->\n");
            bw.write("\t<insert id=\"insertOrUpdateBatch\" parameterType=\"" +
                    Constants.PACKAGE_PO + "." + tableInfo.getBeanName() + "\">\n" +
                    "\t\t<include refid=\"" + INSERT_BATCH_SQL + "\"/>\n" +
                    "\t\tON DUPLICATE KEY UPDATE\n" +
                    insertOrUpdateBufferStr);
            bw.write("\n\t</insert>\n");

            /*基础sql*/
            Map<String, List<FieldInfo>> keyIndexMap = tableInfo.getKeyIndexMap();
            String propertyName = null;
            String javaType = null;
            for (Map.Entry<String, List<FieldInfo>> entry :
                    keyIndexMap.entrySet()) {
                List<FieldInfo> keyFieldInfoList = entry.getValue();
                int index = 0;
                StringBuffer methodName = new StringBuffer();
                for (FieldInfo fieldInfo :
                        keyFieldInfoList) {
                    javaType = fieldInfo.getJavaType();
                    index++;
                    propertyName = fieldInfo.getPropertyName();
                    methodName.append(StringUtils.upperCaseFirstLetter(propertyName));
                    if (index > keyFieldInfoList.size()) {
                        methodName.append("And");
                    }
                }

                bw.write("    <!--根据" + methodName + "查询-->\n" +
                        "    <select id=\"selectBy" + methodName + "\" resultMap=\"" + lowBeanName + "\">\n" +
                        "        SELECT\n" +
                        "        <include refid=\"" + BASE_COLUMN_LIST + "\"/>\n" +
                        "        FROM " + tableInfo.getTableName() + " WHERE " + propertyName + "=#{id}\n" +
                        "    </select>\n");
                bw.write("    <!--根据" + methodName + "更新-->\n" +
                        "    <update id=\"updateBy" + methodName + "\" parameterType=\"" + poClass + "\">\n" +
                        "        UPDATE " + tableInfo.getTableName() + "\n" +
                        "        <set>\n" +
                        "\t\t<include refid=\"" + UPDATE_SQL + "\"/>\n" +
                        "        </set>\n" +
                        "        WHERE " + propertyName + "=#{id}\n" +
                        "    </update>\n");
                // todo 实现批量删除
                if (Constants.IGNORE_COLUMN_DATA) {
                    /*
                     * 直接实现删除操作delete
                     */
                    bw.write("    <!--根据" + methodName + "删除-->\n" +
                            "    <delete id=\"deleteBy" + methodName + "\" parameterType=\"" + javaType + "\">\n" +
                            "        DELETE " + tableInfo.getTableName() + " WHERE " + propertyName + "=#{id}\n" +
                            "    </delete>\n");
                    bw.write("<delete id=\"deleteBy"+methodName+"\" parameterType=\"" + javaType + "\">\n" +
                            "\t\tDELETE FROM "+tableInfo.getTableName()+"  where "+propertyName+" in\n" +
                            "\t\t<foreach collection=\"array\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n" +
                            "\t\t\t#{"+propertyName+"}\n" +
                            "\t\t</foreach>\n" +
                            "\t</delete>");
                } else {
                    /*
                     * 通过更新del_flag的值，实现删除操作update
                     */
                    bw.write("    <!--根据" + methodName + "删除-->\n" +
                            "    <delete id=\"deleteBy" + methodName + "\" parameterType=\"" + javaType + "\">\n" +
                            "        UPDATE " + tableInfo.getTableName() + " SET del_flag = '2' WHERE " + propertyName + "=#{id}\n" +
                            "    </delete>\n");
                    bw.write("    <!--根据" + methodName + "批量删除-->\n" +
                            " \t<delete id=\"deleteBy" + methodName + "s\" parameterType=\"" + javaType + "\">\n" +
                            " \t\tupdate " + tableInfo.getTableName() + " set del_flag = '2' where " + propertyName + " in\n" +
                            " \t\t<foreach collection=\"array\" item=\"id\" open=\"(\" separator=\",\" close=\")\">\n" +
                            " \t\t\t#{"+propertyName+"}\n" +
                            "        </foreach> \n" +
                            " \t</delete>\n");
                }

            }

            bw.write("</mapper>");
            bw.flush();
        } catch (Exception e) {
            logger.error("创建javaBean失败~", e);
        } finally {
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (outw != null) {
                try {
                    outw.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

    }
}
